home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
gnu
/
para.lha
/
para
/
para.el
< prev
next >
Wrap
Text File
|
1991-04-18
|
102KB
|
2,753 lines
;;; para-mode
;;; Copyright (C) 1989, 1990, 1991 Free Software Foundation
;;; ALPHA test version; not for widespead distribution.
;;; Robert J. Chassell
;;; bugs to bob@ai.mit.edu
;;;
;;; Known bug: para-add-node deletes the detailed part of a master
;;; menu if the added node is a chapter. Will need to
;;; modify para-insert-update-pointers-&-menu, using ideas
;;; from para-make-master-menu.
;;;
;;; Change Log ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Version 0.27
;;; 17 apr 91 - Defined `para-main-menu-buffer-name' global variable
;;; with default value of empty string. `para-xref' now
;;; works even without listing the main menu.
;;;
;;; Version 0.26
;;; 5 Apr 91 - Added `para-scroll-down' (bound to DEL); works
;;; parallel to `para-scroll-up' (bound to SPC).
;;;
;;; Version 0.25
;;; 3 Apr 91 - `para-make-file' rewritten.
;;;
;;; Version 0.24
;;; 27 Mar 91 - Indexing commands defined.
;;;
;;; Version 0.23
;;; 25 Mar 91 - `para-menu-explode' defined; forgot to include it before.
;;; `para-preceding-node-type' returns "top" if in top node
;;; and current node if on node line. `para-add-node'
;;; changed to work with new `para-preceding-node-type'.
;;; `para-search' defined; searches forward for regexp.
;;;
;;; Version 0.22
;;; 19 Mar 91 - Rewrote `para-extract-menu-name' to use
;;; `para-extract-menu-node-name' which returns nodename.
;;; This makes mouse menu selection possible in X.
;;;
;;; Version 0.21
;;; 4 Mar 91 - Added `para-history' to `para-goto' when using tag table.
;;; Added `para-tagify' and its utility function,
;;; `para-clean-name' so tag tables can be made.
;;; Removed infinite loop from `para-last'.
;;; Added new movement commands: para-scroll-up,
;;; para-top-node, para-final,
;;; para-goto-previous-node-in-sequence,
;;; para-goto-next-node-in-sequence
;;;
;;; Version 0.20
;;; 26 Feb 91 - Rewrote much of code. No longer dependent on `infosim.el'
;;;
;;; Version 0.10
;;; 12 Feb 91 - Revised all keybindings to be consistent with Texinfo mode.
;;; Merged bug fixes and new menu and file finding commands.
;;; Revised `para.texinfo' doc file.
;;;
;;; <other change logs removed>
;;;
;;; Version 0.01
;;; 17 Dec 1989 -
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; What is Para mode?
;;; Para mode is an extension of Texinfo and Infosim mode that
;;; includes some hypertext-like features. I chose the name `Para
;;; mode' (from the Greek word for `beyond') to suggest that it is
;;; `beyond' Texinfo mode or Infosim mode, but not to claim that it is
;;; a full-fledged hypertext system. Eventually, Para mode should be
;;; merged into Texinfo mode.
;;; Para mode was inspired by Infosim mode, which was written by
;;; Matthew P Wiener.
;;; This code is in `alpha-test'. Please do not distribute it widely.
;;; Summary list of Para mode commands
;
; C-c C-p f para-make-file
; C-c C-p C-q para-quit
;
; C-c C-p a para-add-node
; C-c C-p i para-insert-menu
; C-c C-p e para-menu-explode
; C-c C-p n para-narrow-to-node
;
; C-c C-p m para-make-master-menu (primary updating
; command)
;
; C-c C-p t para-tagify (for faster movement
; in large files)
;
; Directory listing and related movement commands
;
; C-c C-p d para-directory
; C-c C-p l para-list-visited-nodes
;
; C-c C-p C-m para-main-menu-goto-node (these commands parallel
; C-c C-p C-m para-goto-visited-node each other in
; C-c C-p C-m para-menu different buffers)
;
; Other movement commands
;
; C-c C-p C-b para-beginning (beginning of file)
; C-c C-p C-< para-top-node
; C-c C-p C-> para-final (last node in file)
;
; C-c C-p C-u para-up
; C-c C-p C-p para-prev
; C-c C-p C-[ para-goto-previous-node-in-sequence
; C-c C-p C-n para-next
; C-c C-p C-] para-goto-next-node-in-sequence
; C-c C-p RET para-menu (i.e., key chord is `C-p C-c C-m')
; C-c C-p C-l para-last
; C-c C-p C-g para-goto-node
; C-c C-p C-f para-follow
;
; C-c C-p 1 para-menu-1 go to the node of the first menu
; item, etc., up to `9'
;
; C-c C-p C-s para-search
;
; Cross reference creation commands
;
; C-c C-p r para-main-menu-buffer-xref (these commands parallel
; C-c C-p r para-visited-xref each other in
; C-c C-p r para-xref different buffers)
;
; Indexing commands
;
; C-c C-x c para-index-concept (these commands insert
; C-c C-x f para-index-function index entries)
; C-c C-x k para-index-keystroke
; C-c C-x p para-index-program
; C-c C-x t para-index-datatype
; C-c C-x v para-index-variable
;
; C-c C-x x para-insert-index (cause @printindex
; to insert indices)
;
; Texinfo updating commands (other Texinfo commands work also)
;
; C-c C-u C-e texinfo-every-node-update
; C-c C-u C-a texinfo-all-menus-update
;
;;; Load files
;;; The standard Emacs 18 distribution loads version 1 Texinfo files.
;;; Para mode requires version 2 Texinfo files.
;;; To ensure that you load version 2 Texinfo files, you must list the
;;; directory containing the version 2 Texinfo files in your load path
;;; before you list the directory containing the version 1 files. You
;;; could do this by setting your load path in your `.emacs' file in a
;;; manner somewhat similar to the following: (If the first element of
;;; the load path is nil, it signifies the current directory.)
;;; (setq load-path '(nil "/u/texinfo2" "/u/para" "/usr/local/lib/emacs/lisp")
;;; After setting the load path, you may load the necessary Texinfo
;;; files the following commands:
;(load "texinfo")
;(load "texnfo-upd")
;;; Or you may use `require' commands in this `para.el' file, which is
;;; more elegant:
(require 'texinfo)
(require 'texnfo-upd)
(message "Loading Para mode ... ")
;;; (Place `provide' at end of file.)
;;; Syntax table
(defvar para-mode-syntax-table nil)
(if para-mode-syntax-table nil ; based in Infosim mode
(setq para-mode-syntax-table (make-syntax-table))
(modify-syntax-entry ?\" " " para-mode-syntax-table)
(modify-syntax-entry ?\\ " " para-mode-syntax-table)
(modify-syntax-entry ?@ "\\" para-mode-syntax-table)
(modify-syntax-entry ?\^q "\\" para-mode-syntax-table)
(modify-syntax-entry ?\[ "(]" para-mode-syntax-table)
(modify-syntax-entry ?\] ")[" para-mode-syntax-table)
(modify-syntax-entry ?{ "(}" para-mode-syntax-table)
(modify-syntax-entry ?} "){" para-mode-syntax-table)
(modify-syntax-entry ?\' "w" para-mode-syntax-table))
;;; Keybindings
;;; Keybindings are compatible with Texinfo mode,
;;; which are defined in `texinfo.el'.
; TeX commands prefix key: `C-c C-t'
; Elisp formatting commands prefix key: `C-c C-e'
; C code commands prefix key: `C-c C-m'
; Updating commands prefix key: `C-c C-u'
; String insertion commands prefix key: `C-c C-c'
; Para indexing commands prefix key: `C-c C-x'
; Para mode general prefix key: `C-c C-p'
;;; Para mode keys should use a `C-c C-p' prefix, except for
;;; indexing commands and those keys that have special
;;; meaning only in the upper left-hand corner of the window, or in
;;; read-only buffers.
;;; The Para mode keybinding convention is that movement commands use
;;; only control keys and insertion commands use prefix plus regular
;;; key, like the Texinfo string insertion commands.
(defvar para-mode-map nil "Keymap for Para mode.")
;;; !!! remove this setq after finishing testing!
(setq para-mode-map nil)
(if para-mode-map nil
;;; Provide Texinfo mode keybindings;
;;; must be from Texinfo version 2.
(setq para-mode-map (copy-keymap texinfo-mode-map))
(define-key para-mode-map "\t" 'tab-to-tab-stop)
;;; The following keybindings apply only when the cursor is in the
;;; upper left hand corner of the window.
(define-key para-mode-map " " 'para-@)
(define-key para-mode-map "0" 'para-@)
(define-key para-mode-map "1" 'para-@)
(define-key para-mode-map "2" 'para-@)
(define-key para-mode-map "3" 'para-@)
(define-key para-mode-map "4" 'para-@)
(define-key para-mode-map "5" 'para-@)
(define-key para-mode-map "6" 'para-@)
(define-key para-mode-map "7" 'para-@)
(define-key para-mode-map "8" 'para-@)
(define-key para-mode-map "9" 'para-@)
(define-key para-mode-map "b" 'para-@)
(define-key para-mode-map "d" 'para-@)
(define-key para-mode-map "f" 'para-@)
(define-key para-mode-map "g" 'para-@)
(define-key para-mode-map "l" 'para-@)
(define-key para-mode-map "m" 'para-@)
(define-key para-mode-map "n" 'para-@)
(define-key para-mode-map "p" 'para-@)
(define-key para-mode-map "q" 'para-@)
(define-key para-mode-map "s" 'para-@)
(define-key para-mode-map "u" 'para-@)
(define-key para-mode-map "<" 'para-@)
(define-key para-mode-map ">" 'para-@)
(define-key para-mode-map "[" 'para-@)
(define-key para-mode-map "]" 'para-@)
(define-key para-mode-map "\C-?" 'para-@)
;;; `C-c C-p' is the general prefix key for Para mode commands.
;;; Go to first, second, third, etc. node in menu
(define-key para-mode-map "\C-c\C-p1" 'para-menu-1)
(define-key para-mode-map "\C-c\C-p2" 'para-menu-2)
(define-key para-mode-map "\C-c\C-p3" 'para-menu-3)
(define-key para-mode-map "\C-c\C-p4" 'para-menu-4)
(define-key para-mode-map "\C-c\C-p5" 'para-menu-5)
(define-key para-mode-map "\C-c\C-p6" 'para-menu-6)
(define-key para-mode-map "\C-c\C-p7" 'para-menu-7)
(define-key para-mode-map "\C-c\C-p8" 'para-menu-8)
(define-key para-mode-map "\C-c\C-p9" 'para-menu-9)
(define-key para-mode-map "\C-c\C-p0" 'para-menu-1x)
;;; Para mode movement keybindings -- use only control characters:
(define-key para-mode-map "\C-c\C-p\C-b" 'para-beginning)
(define-key para-mode-map "\C-c\C-p\C-f" 'para-follow)
(define-key para-mode-map "\C-c\C-p\C-g" 'para-goto-node)
(define-key para-mode-map "\C-c\C-p\C-l" 'para-last)
(define-key para-mode-map "\C-c\C-p\C-m" 'para-menu)
(define-key para-mode-map "\C-c\C-p\C-n" 'para-next)
(define-key para-mode-map "\C-c\C-p\C-p" 'para-prev)
(define-key para-mode-map "\C-c\C-p\C-q" 'para-quit)
(define-key para-mode-map "\C-c\C-p\C-s" 'para-search)
(define-key para-mode-map "\C-c\C-p\C-u" 'para-up)
(define-key para-mode-map "\C-c\C-p\C-<" 'para-top-node)
(define-key para-mode-map "\C-c\C-p\C->" 'para-final)
(define-key para-mode-map "\C-c\C-p\C-]"
'para-goto-next-node-in-sequence)
(define-key para-mode-map "\C-c\C-p\C-["
'para-goto-previous-node-in-sequence)
;;; Keybindings that create or change a buffer use prefix plus key.
;;; Para mode keybindings for commands to create or change the Para
;;; mode file:
(define-key para-mode-map "\C-c\C-pa" 'para-add-node)
(define-key para-mode-map "\C-c\C-pe" 'para-menu-explode)
(define-key para-mode-map "\C-c\C-pf" 'para-make-file)
(define-key para-mode-map "\C-c\C-pi" 'para-insert-menu)
(define-key para-mode-map "\C-c\C-pm" 'para-make-master-menu)
(define-key para-mode-map "\C-c\C-pr" 'para-xref)
(define-key para-mode-map "\C-c\C-pt" 'para-tagify)
;;; Para mode keybindings for commands to create a temporary buffer
;;; listing all the nodes:
(define-key para-mode-map "\C-c\C-pd" 'para-directory)
(define-key para-mode-map "\C-c\C-pl" 'para-list-visited-nodes)
;;; Exception to C-c C-p keybinding conventions:
(define-key para-mode-map "\C-c\C-pn" 'para-narrow-to-node)
;;; `C-c C-x' is the prefix key for Para indexing.
(define-key para-mode-map "\C-c\C-xc" 'para-index-concept)
(define-key para-mode-map "\C-c\C-xf" 'para-index-function)
(define-key para-mode-map "\C-c\C-xk" 'para-index-keystroke)
(define-key para-mode-map "\C-c\C-xp" 'para-index-program)
(define-key para-mode-map "\C-c\C-xt" 'para-index-datatype)
(define-key para-mode-map "\C-c\C-xv" 'para-index-variable)
(define-key para-mode-map "\C-c\C-xx" 'para-insert-index))
;;; For bindings inside the temporary buffer created by
;;; `para-list-visited-nodes', use:
;;;
;;; C-c C-p r para-visited-xref (based on `para-xref')
;;; C-c C-p C-m para-goto-visited-node (parallel to `para-menu')
;;;
;;; (Note that the keybinding for `para-visited-xref' and
;;; `para-goto-visited-node' are defined locally in the mode map for
;;; the temporary buffer, not in the para-mode-map.)
;;; For bindings inside the temporary buffer created by
;;; `para-directory', use:
;;;
;;; C-c C-p r para-main-menu-buffer-xref (based on `para-xref')
;;; C-c C-p C-m para-main-menu-goto-node (parallel to `para-menu')
;;;
;;; (Keybinding defined in local keymap.)
;;; Mode line for Para mode
(defvar para-ignore-extension nil
"*If non-nil, visited filename extension not shown in mode-line.")
(defun para-set-mode-line ()
(let ((filename (file-name-nondirectory (buffer-file-name))))
(if para-ignore-extension
(setq filename
(substring filename 0 ; name sans extension
(or (string-match "\\.tex" filename)
(length filename)))))
(setq mode-line-modified '("--%1*%1*-")
mode-line-buffer-identification
(concat "Para mode: ("
filename
")"
(or para-current-node "")
" "))))
;;; Para mode
(defun para-mode ()
"Hypertext-like extension of Texinfo.
Provides editing and Info-like movement commands for Texinfo files.
When point is at the upper-left corner, or if the buffer is read-only,
Emacs interprets key strokes as Info-like commands. Otherwise,
keystrokes self-insert.
The default keybindings are compatible with
Texinfo mode. The prefix keys for the different groups of commands
are:
TeX commands prefix key: `C-c C-t'
Elisp formatting commands prefix key: `C-c C-e'
Makeinfo commands prefix key: `C-c C-m'
Updating commands prefix key: `C-c C-u'
String insertion commands prefix key: `C-c C-c'
Para indexing commands prefix key: `C-c C-x'
Para mode general prefix key: `C-c C-p'
Primary movement commands are similar to those in Info:
\\<para-mode-map>
Display the first menu: \\[para-directory]
Go to the `Next' node: \\[para-next]
Go to the `Previous' node: \\[para-prev]
Go to the `Up' node: \\[para-up]
Go to the menu item named: \\[para-menu]
Primary editing features are:
Create a file in Para mode
and insert its header: \\[para-make-file]
Create a new node
and section header: \\[para-add-node]
Insert a Texinfo menu template: \\[para-insert-menu]
Create new node and section
headers at the end of the
current node, based on menu: \\[para-menu-explode]
Insert Texinfo cross reference: \\[para-xref]
Make a master menu: \\[para-make-master-menu]
The commands are:
\\{para-mode-map}"
(interactive)
(text-mode)
(kill-all-local-variables)
(use-local-map para-mode-map)
(set-syntax-table para-mode-syntax-table)
(setq mode-name "Para"
major-mode 'para-mode)
(make-variable-buffer-local 'para-main-menu-completions-list)
(make-local-variable 'require-final-newline)
(make-local-variable 'paragraph-separate)
(make-local-variable 'paragraph-start)
(make-local-variable 'fill-column)
(make-local-variable 'comment-start)
(make-local-variable 'comment-start-skip)
(make-local-variable 'para-history)
(make-local-variable 'para-current-node)
(make-local-variable 'para-ignore-extension)
(make-local-variable 'para-last-search)
(setq require-final-newline t
paragraph-separate (concat "^\b\\|^@[a-zA-Z]*[ \n]\\|"
paragraph-separate)
paragraph-start (concat "^\b\\|^@[a-zA-Z]*[ \n]\\|" paragraph-start)
fill-column 72
comment-start "@c \|@comment "
comment-start-skip "@c +\|@comment +")
(para-narrow-to-node)
(run-hooks 'text-mode-hook 'texinfo-mode-hook 'para-mode-hook))
;;; Make a Para mode file; or quit.
;;; C-c C-p f para-make-file Create a file in Para mode
;;; C-c C-p C-q para-quit Change to Texinfo mode
(defun para-make-file (filename title node-name chapter-name)
"Create a file in Para mode and insert its header.
Filename should end in `.tex', `.texi', or `.texinfo'. extension.
This command inserts the `setfilename' and `settitle' commands used by
Texinfo. Prompts user for FILENAME, TITLE used by TeX, first
NODE-NAME, and first CHAPTER-NAME.
The Info file name is generated from the FILENMAE by removing the
extension and replacing it with `.info'. If the file already exists
an error is signaled."
(interactive
(list
(read-from-minibuffer "File name: ")
(read-from-minibuffer "Title: ")
(read-from-minibuffer "Name for first node: ")
(read-from-minibuffer "Name for first chapter: ")))
(if (file-exists-p filename)
(error "File `%s' already exists!" filename))
(find-file filename)
(set-syntax-table para-mode-syntax-table)
(goto-char (point-min))
(let ((info-filename
(concat
(file-name-nondirectory
(substring
filename
0
(or (string-match "\\.tex" filename) (length filename))))
".info")))
;; Format:
;; \input texinfo @c -*-para-*-
;; @setfilename INFO-FILENAME
;; @settitle TITLE
;;
;; @node top,
;;
;; @node NODE-NAME
;; @chapter CHAPTER-NAME
;; ...
;; @bye
(insert "\\input texinfo @c -*-para-*-\n")
(insert (format "@setfilename %s\n" info-filename))
(insert (format "@settitle %s\n\n" title))
(insert "@node top, \n\n")
(insert (format "@node %s,\n" node-name))
(insert (format "@chapter %s\n\n\n\n" chapter-name))
(insert "@bye\n")
(para-mode)
(forward-line -3)
(widen)))
(defun para-quit ()
"Exit para mode"
(interactive)
(widen)
(texinfo-mode))
;;; Add a new node
;;; C-c C-p a para-add-node
(defun para-add-node (new-node-type name title)
"Create new node and section headers.
In interactive use,
1. No prefix argument: section type defaults to the same type as
the preceding node.
2. With prefix arg, obtain a prompt for the section type. A
default is offered, which one level lower than the preceding
node (unless the previous node is already at the lowest level).
You are prompted for the node name and section title.
If the new node is
* at the same or lower hierarchical level than the preceding node,
the function inserts new headers at point;
* at a higher hierarchical level than the preceding node,
the function inserts new headers just before next node of
the new level, or at the end of the buffer.
The function updates the appropriate node lines and the associated
menu, but not the main menu (unless the new node is a chapter).
In non-interactive use, the first argument is the TYPE or level of the
new node; the second argument is the the node NAME and the and third
argument is the section TITLE."
(interactive
(if (not current-prefix-arg)
(let ((preceding-node-type (para-preceding-node-type)))
;; top node case
(if (string= preceding-node-type "top")
(setq preceding-node-type "chapter"))
(list preceding-node-type
(read-string
(format "Node name for `%s' (end with RET): "
(para-preceding-node-type)))
(read-string "Title (end with RET): ")))
;; Ask what new node level should be; this becomes the
;; value of new-node-type even though it starts out as
;; the prefix arg
(list
(read-no-blanks-input
"New node level: "
(cdr (assoc (para-preceding-node-type) para-subtype-alist)))
(read-string "Node name (end with RET): ")
(read-string "Title (end with RET): "))))
(let* ((preceding-node-type (para-preceding-node-type))
(new-node-general-type
(cdr (assoc preceding-node-type texinfo-section-to-generic-alist))))
;; top node case
(if (string= preceding-node-type "top")
(setq preceding-node-type "chapter"
new-node-general-type "chapter"))
;; Adding new nodes has three cases:
;;
;; 1. New node is at same level as preceding node:
;; insert it at point.
;;
;; 2. New node is at lower level than preceding node:
;; also, insert it at point.
;;
;; 3. New node is at higher level than preceding node:
;; insert it at end of the preceding node of the same level
;; after any intervening nodes; i.e. put it just before the
;; next node of that level.
(if (not new-node-type)
;; 1. most common case; insert same level node at point
(insert
(format "\n@node %s,\n@%s %s\n" name preceding-node-type title))
;; Else insert a node at a different level:
(setq new-node-general-type
(cdr
(assoc
new-node-type texinfo-section-to-generic-alist)))
;; 2. If the node is at a same or lower level, insert at point;
;; but if the node is at a higher level, insert it just before
;; next node of that level.
(if (node-same-or-lower-level-p new-node-type preceding-node-type)
(insert (format "\n@node %s,\n@%s %s\n" name new-node-type title))
;; 3. Else the new node level is higher then the preceding node.
;; Find next node of that level:
(if (re-search-forward
(concat
"\\(^@node\\).*\n" ; match node line
"\\(\\(^@c\\|^@if\\).*\n\\)?" ; match comment or
; ifinfo line, if any
(eval
(cdr ; return regexp for search
(assoc
new-node-general-type
texinfo-update-menu-same-level-regexps))))
nil
'goto-end)
(goto-char (match-beginning 0))
(point-max))
(insert
(format "\n@node %s,\n@%s %s\n\n" name new-node-type title))
(forward-line -1)))
;; Finally, update relevant pointers and menu
(para-insert-update-pointers-&-menu new-node-general-type)
(message "Added new node: name, `%s'; title, `%s' ... Done."
name title)))
;;; Utility functions for `para-add-node'
(defun para-preceding-node-type ()
"Return type of preceding node as string, eg \"section\" or \"chapter\"."
(save-excursion
(end-of-line)
(if (re-search-backward "^@node" nil t)
(if (looking-at "^@node [ \t]*top")
;; top node case: return top
"top"
;;else
(if (re-search-forward
(concat
"\\(^@node\\).*\n" ; match node line
"\\(\\(^@c\\|^@if\\).*\n\\)?" ; match comment or
; ifinfo line, if any
"\\(^@\\).*") ; match title line
nil
t)
(buffer-substring
(progn (beginning-of-line) ; copy its name
(1+ (point)))
(progn (forward-word 1)
(point)))))
;; else no node line found
(error "No preceding node!"))))
(defun node-same-or-lower-level-p (new-node-type preceding-node-type)
"t if NEW-NODE-TYPE at same or lower level as PRECEDING-NODE-TYPE."
(let ((new-node-general-type
(cdr (assoc new-node-type texinfo-section-to-generic-alist)))
(node-sequence
(assoc ; returns list of same and lower types
(cdr (assoc
preceding-node-type
texinfo-section-to-generic-alist))
para-matching-subtypes-alist)))
(node-in-sequence-p new-node-general-type node-sequence)))
(defun node-in-sequence-p (node node-sequence)
"Return t if NODE is at same or lower level in NODE-SEQUENCE.
The node sequence is a list of strings of the general type of node,
such as `subsection' or `unnumbered'."
(if node-sequence
(if (string= node
(car node-sequence))
t
(node-in-sequence-p node (cdr node-sequence)))))
;;; `para-add-node' utility; goto node of specified general type
(defun para-goto-node-of-type (type direction)
"Move point to node of specified general TYPE in specified DIRECTION.
Return nil if specified node not found, otherwise, move point."
(widen)
(let ((search-function
(cond ((eq direction 'next) 're-search-forward)
((eq direction 'previous) 're-search-backward))))
(if (string= "chapter" type)
(if
(funcall search-function
(concat
"\\("
"\\(^@node [ \t]*top\\)" ; top node case
"\\|" ; or
"\\(^@node\\).*\n" ; match node line
"\\(\\(^@c\\|^@if\\).*\n\\)?" ; match comment
; or ifinfo line, if any
"\\(^@" ;match chapter line
texinfo-chapter-level-regexp
"\\)"
"\\)")
nil
t)
(goto-char (match-beginning 0))
;; else node not found
nil)
;;else not a chapter
(if (funcall search-function
(concat
"\\(^@node\\).*\n" ; match node line
"\\(\\(^@c\\|^@if\\).*\n\\)?" ; match comment
; or ifinfo line, if any
(eval
(cdr
(assoc type texinfo-update-menu-same-level-regexps))))
nil
t)
(goto-char (match-beginning 0))
;; else node not found
nil))))
;;; `para-add-node' utility; insert and update pointers and menu
(defun para-insert-update-pointers-&-menu (new-node-general-type)
"Insert pointers of an added node, update other pointers and menu.
Specifically, insert the Next, Previous, and Up pointers of the current
node, update the pointers of the Next and Previous nodes, and update
the menu for the added node."
(let ((auto-fill-hook nil))
(save-excursion
;; First, update current node:
(if (not (re-search-backward "^@node" (point-min) t))
(error "Node line not found before this position."))
(texinfo-update-the-node)
;; Second, update previous node:
(save-excursion
(if (para-goto-node-of-type new-node-general-type 'previous)
(texinfo-update-the-node)))
;; Third, update next node:
(save-excursion
(if (para-goto-node-of-type new-node-general-type 'next)
(texinfo-update-the-node)))
;; Finally, update the menu for this node
(save-excursion
(texinfo-make-one-menu new-node-general-type)))))
;;; Add new nodes based on menu: `para-menu-explode'
(defun para-menu-explode ()
"Based on menu, create new node and section headers at end of current node.
Each node name is extracted from a menu entry, and the corresponding
section title is the same as the descriptive string in that menu
entry."
(interactive)
(if (re-search-backward "^@node" (point-min) t)
(let* ((up-node (texinfo-copy-node-name))
(new-node-type
(cdr (assoc (para-preceding-node-type) para-subtype-alist)))
(new-node-general-type
(cdr (assoc new-node-type
texinfo-section-to-generic-alist)))
menu-end menu next-node previous-node)
(if (string= new-node-general-type "subsubsection")
(error "This node is too deep to create subnodes."))
(if (search-forward "\n@menu" nil t)
(save-excursion
(if (search-forward "\n@end menu" nil t)
(setq menu-end (point))
(error "No \"@end menu\" found!")))
(error "No menu found!"))
;; create list of node names and titles
(while (not (looking-at "^@end menu"))
(setq menu
(cons
(cons
(para-extract-menu-node-name)
(para-main-menu-buffer-copy-description menu-end))
menu))
(forward-line 1))
(setq menu (reverse menu)
next-node (append (cdr menu) (list (cons "" "")))
previous-node (append (list (cons "" "")) menu))
(if (re-search-forward "^@node\\|^@bye" (point-max) 'move)
(forward-line -1))
(while menu
(insert
(format
"\n@node %s, %s, %s, %s\n@%s %s\n"
(car (car menu)) (car (car next-node))
(car (car previous-node)) up-node
new-node-type (cdr (car menu))))
(setq menu (cdr menu)
next-node (cdr next-node)
previous-node (cdr previous-node)))
(recenter -1))
(error "Node line not found before this position.")))
;;; Insert a Texinfo cross reference
;;; C-c C-p r para-xref
(defun para-xref (insert-topic-p nodename description)
"Insert a Texinfo cross reference.
The first argument, if non-nil, means incorporate topic description in
reference. In interactive use, this argument is optional prefix
argument. The second argument is the NODENAME for the reference, the
third argument is the topic DESCRIPTION.
If `para-main-menu-completions-list' is non-nil, completion for node
names in the list. \(No default offered---you have to type the
beginning of the node name. Type \\[para-directory] to generate a
`para-main-menu-completions-list' if it does not exist.\)"
(interactive
(let* ((completion-ignore-case t)
(insert-topic-p current-prefix-arg)
(node-name
(if (get-buffer para-main-menu-buffer-name)
(save-excursion
(set-buffer para-main-menu-buffer-name)
(completing-read
"Node name (with completion): "
para-main-menu-completions-list))
(read-string "Node name: ")))
(description
(if insert-topic-p
(read-string "Topic description (end with RET): "
(if (get-buffer para-main-menu-buffer-name)
(save-excursion
(set-buffer para-main-menu-buffer-name)
(goto-char (point-min))
(if (re-search-forward
(regexp-quote node-name)
nil t)
(progn
(beginning-of-line)
(search-forward ":")
(para-main-menu-buffer-copy-description
(point-max)))
""))
""))
"")))
(list insert-topic-p node-name description)))
(if (string= "" description)
(insert (format "@xref{%s}." nodename))
(insert (format "@xref{%s, , %s}." nodename description))))
;;; Chapter, section, etc. types alists
(defvar para-matching-subtypes-alist
'(("top" . ("chapter" "section" "subsection" "subsubsection"))
("chapter" . ("section" "subsection" "subsubsection"))
("section" . ("subsection" "subsubsection"))
("subsection" . ("subsubsection"))
("subsubsection" . nil))
"*Alist matchining each general section type with a list of its same
and sub types.")
(defvar para-subtype-alist
'(("top" . "chapter")
("chapter" . "section")
("unnumbered" . "unnumberedsec")
("majorheading" . "heading")
("chapheading" . "heading")
("appendix" . "appendixsec")
("section" . "subsection")
("unnumberedsec" . "unnumberedsubsec")
("heading" . "subheading")
("appendixsec" . "appendixsubsec")
("subsection" . "subsubsection")
("unnumberedsubsec" . "unnumberedsubsubsec")
("subheading" . "subsubheading")
("appendixsubsec" . "appendixsubsubsec")
("subsubsection" . "subsubsection")
("unnumberedsubsubsec" . "unnumberedsubsubsec")
("subsubheading" . "subsubheading")
("appendixsubsubsec" . "appendixsubsubsec"))
"*Alist of section types and their subtypes.")
;;; Narrow to node
;;; C-c C-p n para-narrow-to-node
(defun para-narrow-to-node ()
"Narrow to current node."
(interactive)
(let ((current-position (point))
end-of-node-name
beginning-of-node)
(widen)
(beginning-of-line)
(if (or (looking-at "^@node")
(re-search-backward "^@node" nil 'move-to-limit))
;; point is within a node
(progn
(re-search-forward "@node[ \t]*\\<\\([^,]*\\),")
(setq end-of-node-name
(point)
para-current-node
(buffer-substring (match-beginning 1) (match-end 1)))
(beginning-of-line)
(while (looking-at "^@")
(forward-line -1))
(forward-line 1)
(setq beginning-of-node (point))
;; skip past beginning node name
(goto-char end-of-node-name))
;; else point is before first node
(setq beginning-of-node 1
para-current-node "Beginning of Buffer"))
(para-set-mode-line)
;; search for next node
(if (search-forward "\n@node" nil 'move-to-limit)
(progn
(beginning-of-line)
(while (looking-at "^@")
(forward-line -1))))
(narrow-to-region beginning-of-node (point))
(goto-char current-position)))
;;; Goto a node.
(defun para-goto-node (name)
"Go to NAME, which may be `nodename' or `(filename)nodename'.
This is an interface to the function `para-goto'."
(interactive "sGoto: ")
(if (not (para-goto name)
(error "Node not found: %s " name))))
(defun para-goto (name)
"Go to NAME, which may be `nodename' or `(filename)nodename'.
Use `para-goto-node' interactively."
;; If the string passed as `name' starts with a left parenthesis,
;; then the text within parentheses is the name of a file
(let (filename nodename)
(if (string-equal (substring name 0 1) (char-to-string ?\())
;; then name starts with filename
(setq filename
(substring name 1 (string-match (char-to-string ?\)) name))
nodename
(substring name (1+ (string-match (char-to-string ?\)) name))))
;; else name is nodename only
(setq filename nil
nodename name))
(cond
;; if file name is `dir', go to Info directory
((and filename
(string= (downcase filename) "dir")) (Info-directory))
(filename ; i.e., if there is a filename
(if (string-equal nodename "") ; go to file, which will
; enter para mode if it is
; that type
(switch-to-buffer (find-file-noselect filename))
;; else go to a node in the file \(must be a Para mode file\)
(find-file filename)
(widen)
(goto-char (point-min))
(para-goto nodename)))
((string-equal nodename "*")
(para-beginning)
(setq para-current-node nodename)
(setq para-history (cons para-current-node para-history))
(para-set-mode-line))
;; else must be a node to go to
(t
(let ((current-position (point)))
(widen)
(goto-char (point-max))
;; if both a tag table and find node, goto it
(if (and
(search-backward "End tag table\n@end ignore" nil t)
(para-tag-table-find))
;; then
(progn
(para-narrow-to-node)
;; para-narrow-to-node specifies value of para-current-node
(setq para-history (cons para-current-node para-history))
(para-set-mode-line)
t)
;; else either no tag table or node not in tag table
(if (re-search-backward
(concat "^@node [ \t]*" nodename) nil t)
;; then
(progn
(para-narrow-to-node)
(setq para-history (cons para-current-node para-history))
(para-set-mode-line)
t)
;; else could not find new node, return to current position
(goto-char current-position)
(para-narrow-to-node)
nil)))))))
(defun para-tag-table-find ()
"Use a tag table to assist in going to a node quickly.
Tag table must be at the end of a Para mode file in an @ignore.
Point must be closer to the end than the beginning of the @ignore."
(and (search-backward "@ignore\nTag table:" nil t)
(search-forward (concat "Node: " nodename "\177") (point-max) t)
(para-tag-search nodename (read (current-buffer)) 500)))
(defun para-tag-search (node point chunk)
"Search for NODENAME from POINT, forward and back in CHUNK-sized spans."
(let ((forward-search-position point)
(backward-search-position point)
(target-node (concat "^@node[ \t]+" (regexp-quote node) "[ \t]*,"))
(max-limit (point-max))
forward-limit backward-limit found)
(while (not found)
(setq forward-limit
(min max-limit (+ forward-search-position chunk))
backward-limit
(max 1 (- backward-search-position chunk)))
(goto-char forward-search-position)
(if (and (< forward-search-position max-limit)
(re-search-forward target-node forward-limit t))
(progn
(beginning-of-line)
(setq found (point))) ; and exit while loop
;;else
(goto-char backward-search-position)
(if (and (< 1 backward-search-position)
(re-search-backward target-node backward-limit t))
(setq found (point)) ; and exit while loop
;; else
(if (and (= forward-search-position max-limit)
(= backward-search-position (point-min)))
(setq found 'at-limits) ; and exit while loop
;; else repeat while loop
(setq forward-search-position forward-limit
backward-search-position backward-limit)))))
(if (numberp found)
(goto-char found)
nil)))
;;; Tagify a Para mode file
(defun para-tagify ()
"Create or update a Para mode tag table in the current buffer."
(interactive)
;; Save and restore point and restrictions.
;; `save-restriction' would not work
;; because it records the old max relative to the end.
;; Record it relative to the beginning.
(let ((old-minimum (point-min))
(old-maximum (point-max))
(no-max (= (point-max) (1+ (buffer-size))))
(old-point (point)))
(unwind-protect
(progn
(widen)
(goto-char 1)
(let ((case-fold-search t)
list)
(while (search-forward "\n@node" nil t)
(forward-line -1)
(let ((beg (point)))
(forward-line 2)
(if (re-search-backward "^@node[ \t]+\\([^,]+\\)," beg t)
(setq list
(cons (list (para-clean-name
(buffer-substring
(match-beginning 1)
(match-end 1))
" \t")
beg)
list)))))
(goto-char (point-max))
(let ((buffer-read-only nil))
(if (not (search-backward "\n@bye" nil t))
(error "This file requires an `@bye'!")
;; else removing existing tag table if there is one
(if (search-forward "\nEnd tag table\n@end ignore\n" nil t)
(let ((end (point)))
(search-backward "\nTag table:\n")
(beginning-of-line)
(delete-region (point) end))))
(goto-char (point-max))
(insert "\n\n@ignore\nTag table:\n")
(setq list (nreverse list))
(while list
(insert "Node: " (car (car list)) ?\177)
(princ (car (cdr (car list))) (current-buffer))
(insert ?\n)
(setq list (cdr list)))
(insert "\nEnd tag table\n@end ignore\n")))))
(goto-char old-point)
(narrow-to-region old-minimum (if no-max (1+ (buffer-size))
(min old-maximum (point-max))))))
(defun para-clean-name (string chars-to-remove)
"Remove leading/trailing characters from STRING that match CHARS-TO-REMOVE."
(if (string= chars-to-remove "") string
;; else
(let* ((i 0)
(regex-to-remove (concat "[" chars-to-remove "]"))
(dont-remove (concat "[^" chars-to-remove "]"))
;; delete leading chars-to-remove
(string (substring string (or (string-match dont-remove string) 0)))
(l (length string)))
(while (and (< i l) (string-match regex-to-remove (substring string -1)))
(setq i (1+ i) string (substring string 0 -1)))
string)))
;;; Insert a menu.
;;; C-c C-p i para-insert-menu
(defun para-insert-menu ()
"Insert a Texinfo menu template and position point within it."
(interactive)
(insert "@menu\n* ::\t\t\n@end menu\n")
(forward-line -2)
(forward-char 2))
;;; Make a Master Menu
;;; C-c C-p m para-make-master-menu
;;; `para-master-menu' is a rewrite of texinfo-master-menu and,
;;; perhaps, should replace that function. What is new is that
;;; para-master-menu removes a pre-existing detailed part of the
;;; master menu---but if the pre-existing master menu uses a
;;; non-standard `texinfo-master-menu-header', this removal fails.
;;; Also, para-master-menu removes extra blank lines that the menu
;;; insert functions insert (and should insert, lest there be no blank
;;; line separates when you make the first master menu).
;;; Replace following with texinfo-master-menu? Yes, but also provide
;;; an indented master menu option.
;;; Currently, `texinfo-master-menu' works like this:
;;; "Make a master menu for a whole Texinfo file.
;;;Non-nil argument (prefix, if interactive) means first update all
;;;existing nodes and menus. Remove pre-existing master menu, if there is one.
(defun para-make-master-menu
(&optional update-all-nodes-menus-p para-indented-master-menu-p)
"Make a master menu for a whole Para mode file.
In interactive use:
1. With no prefix arg, make a regular master menu, first removing
detailed part of pre-existing master menu, if there is one.
2. With prefix arg, first update all existing nodes and menus
\(incorporating descriptions from pre-existing menus\), first
removing detailed part of pre-existing master menu, if there is
one.
3. With numeric prefix arg, insert an indented master menu. All
the nodes are in sequence.
4. With negative numeric prefix arg, first update all existing
nodes and menus \(incorporating descriptions from pre-existing
menus\). First remove detailed part of pre-existing master
menu, if there is one, then insert an indented master menu.
When called from a program, non-nil optional first arg means first
update all existing nodes and menus \(incorporating descriptions from
pre-existing menus\); non-nil optional second arg means insert an
indented master menu in which all the nodes are in sequence.
This function creates a menu that follows the top node. It replaces
any existing menu there, first removing the detailed part of an
already existing master. \(This action depends on the pre-exisitng
master menu using the standard `texinfo-master-menu-header'.\)
A regular master menu has the following format, which is adapted from the
recommendation in the Texinfo Manual:
* The first part contains the major nodes in the Texinfo file: the
nodes for the chapters, chapter-like sections, and the major
appendices. This includes the indices, so long as they are in
chapter-like sections, such as unnumbered sections.
* The second and subsequent parts contain a listing of the other,
lower level menus, in order. This way, an inquirer can go
directly to a particular node if he or she is searching for
specific information.
Each of the menus in the detailed node listing is introduced by the
title of the section containing the menu.
An indented master menu consists of all the menu entries in the
buffer, in sequence, with lower level menus indented according to
their hierarchical level."
(interactive
(cond ((not current-prefix-arg) (list nil nil))
((listp current-prefix-arg) (list t nil))
((> (prefix-numeric-value current-prefix-arg) 0) (list nil t))
((< (prefix-numeric-value current-prefix-arg) 0) (list t t))))
(widen)
(goto-char (point-min))
;; Move point to location after `top'.
(if (not (re-search-forward "^@node [ \t]*top[ \t]*\\(,\\|$\\)" nil t))
(error "This buffer needs a Top node!"))
(let ((first-chapter
(save-excursion (re-search-forward "^@node") (point))))
(if (re-search-forward texinfo-master-menu-header first-chapter t)
;; Remove detailed master menu listing
(progn
(goto-char (match-beginning 0))
(let ((end-of-detailed-menu-descriptions
(save-excursion ; beginning of end menu line
(goto-char (texinfo-menu-end))
(beginning-of-line) (forward-char -1)
(point))))
(delete-region (point) end-of-detailed-menu-descriptions)))))
(if update-all-nodes-menus-p
(progn
(message "Making a master menu...first updating all nodes... ")
(sleep-for 2)
(mark-whole-buffer)
(texinfo-update-node t)
(message "Updating all menus... ")
(sleep-for 2)
(mark-whole-buffer)
(texinfo-make-menu t)))
(message "Now making the master menu... ")
(sleep-for 2)
(goto-char (point-min))
(if para-indented-master-menu-p
(progn
(para-insert-indented-master-menu)
(message
"Done...completed making indented master menu. You may save the buffer."))
;; else
(texinfo-insert-master-menu-list
(texinfo-master-menu-list))
;; Remove extra newlines that texinfo-insert-master-menu-list
;; may have inserted.
(save-excursion
(goto-char (point-min))
(re-search-forward texinfo-master-menu-header)
(goto-char (match-beginning 0))
(insert "\n")
(delete-blank-lines)
(re-search-backward "^@menu")
(forward-line -1)
(delete-blank-lines)
(re-search-forward "^@end menu")
(forward-line 1)
(delete-blank-lines)
(message
"Done...completed making master menu. You may save the buffer."))))
;;; Find node in current buffer or other file with `para-menu'.
;;; C-c C-p C-m para-menu
; The format must be one of following:
; * nodename:: description
; * item:nodename. description
; * item:(filename). description
; * item:(filename)nodename. description
; (The `description' is optional.)
;
; The file name must either be to a file in the current directory or a
; full, absolute pathname.
;
; To go to the item, position the cursor at the beginning of the line
; for the item and type `C-c C-p C-m' and then type a RET to confirm
; the default item.
(defun para-menu (menu-item)
"Go to node for menu item named (or abbreviated) NAME.
In interactive use, default is node under cursor.
Completion for any name in this menu."
(interactive
(list
(let (completions
(p (point))
(default
(save-excursion
(beginning-of-line)
(if (re-search-forward "\\* \\([^:\t\n]*\\):" nil t)
(buffer-substring (match-beginning 1) (match-end 1))
(prog1 nil (message "No default menu item found."))))))
(save-excursion
(goto-char (point-min))
(if (not (search-forward "\n@menu" nil t))
(error "No menu in this node.")
;; create alist for completing-read
(while (re-search-forward
"\\* \\([^:\t\n]*\\):" nil t)
;; make a list,
;; each of element of which is \("name" . position\)
(setq completions (cons (cons (buffer-substring
(match-beginning 1)
(match-end 1))
(match-beginning 1))
completions)))
(let (item)
(setq item (let ((completion-ignore-case t))
(completing-read
(if default
(format "Menu item (default %s): " default)
"Menu item: ")
completions nil t)))
(if (string= item "") default item)))))))
(save-excursion
(goto-char (point-min))
(or (search-forward "\n@menu" nil t)
(error "No menu in this node"))
(para-goto (para-extract-menu-name menu-item))))
(defun para-extract-menu-name (menu-item)
"Return `nodename' or `(filename)nodename' for MENU-ITEM.
Point must be on or before named menu-item."
(beginning-of-line)
(or (re-search-forward (concat "* " "\\(" menu-item "\\)") nil t)
(error "No such item in menu"))
(para-extract-menu-node-name))
(defun para-extract-menu-node-name ()
"Return `nodename' or `(filename)nodename'
Point must be on or before menu line."
;;; Four circumstances:
;;; * nodename:: description
;;; * item:nodename. description
;;; * item:(filename). description
;;; * item:(filename)nodename. description
;;; If a nodename is the second part of a menu item, you may follow it
;;; with a period, comma, tab, or newline.
(beginning-of-line)
(or (re-search-forward (concat "* \\(\\w+\\)") nil t)
(error "Cannot find a menu line."))
(let ((start-of-name (match-beginning 1))
node)
(if (and
(search-forward ":" nil t)
(looking-at ":")) ; look at `::'
(setq node
(buffer-substring start-of-name (1- (point)))) ; return nodename
;;else
(skip-chars-forward ": \t")
(setq start-of-name (point))
(if (search-forward "(" nil t) ; skip over node name, if any
(progn (goto-char (1- (point)))
(forward-sexp)))
(re-search-forward "[.,\t\n]" nil t)
;; return either `nodename',`(filename)', or `(filename)nodename'
(setq node (buffer-substring start-of-name (1- (point)))))
node))
;;; Goto the first, second, third node in a menu.
;;; C-c C-p 1 para-menu-1 go to the node of the first menu
;;; item, etc., up to `9'
;;; Go to first, second, third etc. menu item
(defun para-menu-1 ()
"Go to the node of the first menu item."
(interactive)
(if (not (para-goto (para-menu-count 1)))
(error "Node not found.")))
(defun para-menu-2 ()
"Go to the node of the second menu item."
(interactive)
(if (not (para-goto (para-menu-count 2)))
(error "Node not found.")))
(defun para-menu-3 ()
"Go to the node of the third menu item."
(interactive)
(if (not (para-goto (para-menu-count 3)))
(error "Node not found.")))
(defun para-menu-4 ()
"Go to the node of the fourth menu item."
(interactive)
(if (not (para-goto (para-menu-count 4)))
(error "Node not found.")))
(defun para-menu-5 ()
"Go to the node of the fifth menu item."
(interactive)
(if (not (para-goto (para-menu-count 5)))
(error "Node not found.")))
(defun para-menu-6 ()
"Go to the node of the sixth menu item."
(interactive)
(if (not (para-goto (para-menu-count 6)))
(error "Node not found.")))
(defun para-menu-7 ()
"Go to the node of the seventh menu item."
(interactive)
(if (not (para-goto (para-menu-count 7)))
(error "Node not found.")))
(defun para-menu-8 ()
"Go to the node of the eighth menu item."
(interactive)
(if (not (para-goto (para-menu-count 8)))
(error "Node not found.")))
(defun para-menu-9 ()
"Go to the node of the ninth menu item."
(interactive)
(if (not (para-goto (para-menu-count 9)))
(error "Node not found.")))
(defun para-menu-1x (count)
"Go to the prompted for menu item number."
(interactive "nMenu item #: ")
(if (not (para-goto (para-menu-count count)))
(error "Node not found.")))
(defun para-menu-count (count)
(let (name)
(save-excursion
(goto-char (point-min))
(or (search-forward "\n@menu" nil t)
(error "No menu in this node"))
(or (re-search-forward "\n[ ]*\\* \\([^:\t\n]*\\):" nil t count)
(error "Too few items in menu"))
(beginning-of-line)
(if (setq name (para-extract-menu-name
(buffer-substring (match-beginning 1) (match-end 1))))
name
(error "Node not found.")))))
;;; Display the first menu in a temporary buffer using `para-directory'
;;; C-c C-p d para-directory
(defvar para-main-menu-buffer-name ""
"Temporary buffer used by `para-directory' for main menu.
Also, used by `para-xref' and `para-visited-xref' for cross reference
completion.")
(defun para-directory ()
"Display the first menu in a temporary buffer.
Usually, the first menu is the master menu.
In this buffer,
\\<para-main-menu-buffer-mode-map>
`para-main-menu-goto-node' is bound to \\[para-main-menu-goto-node]
which will jump you to the prompted for node in the Para Mode file.
Default is the name of the node under the cursor. Completion offered.
Also,
`para-main-menu-buffer-xref' is bound to \\[para-main-menu-buffer-xref]
which inserts a Texinfo cross reference in the associated Para mode
file at the current position of point in the Para mode file. Default
is the name of the node under the cursor. With optional argument
\(prefix if interactive\), Emacs prompts you for a topic description.
Completion is offered, but only for node names listed in this
temporary buffer."
(interactive)
(setq para-current-buffer (current-buffer))
(setq para-main-menu-buffer-name
(concat para-main-menu-prefix (buffer-name) " "))
(let (this-node
(this-line 0))
(save-excursion
(save-restriction
(widen)
;; Record current node name for later positioning in buffer.
(end-of-line)
(if (re-search-backward "^@node" nil t)
(setq this-node (texinfo-copy-node-name))
(setq this-node nil))
(goto-char (point-min))
(if (not (re-search-forward "^@menu"))
(error "Menu not found!"))
(forward-line 1)
(let ((menu
(buffer-substring
(point)
(save-excursion
(if (not (re-search-forward "^@end menu" nil t))
(error "End of menu not found!"))
(beginning-of-line)
(point)))))
(with-output-to-temp-buffer para-main-menu-buffer-name
(set-buffer para-main-menu-buffer-name)
(kill-all-local-variables)
(set-syntax-table para-mode-syntax-table)
(use-local-map para-main-menu-buffer-mode-map)
(setq mode-name "Para Menu"
major-mode 'para-main-menu-buffer-mode)
(insert menu)
(setq buffer-read-only t)
;; create alist for completing-read
(goto-char (point-min))
(while (re-search-forward
"\n[ ]*\\* \\([^:\t\n]*\\):" nil t)
;; make a list,
;; each of element of which is \("name" . position\)
(setq para-main-menu-completions-list
(cons (cons (buffer-substring
(match-beginning 1)
(match-end 1))
(match-beginning 1))
para-main-menu-completions-list)))
(goto-char (point-min))
))))
(pop-to-buffer para-main-menu-buffer-name)
(and this-node
(re-search-forward this-node nil t) (beginning-of-line))))
;;; Utility functions for displaying first menu
;;; Includes commands to go to the node under the cursor and to insert
;;; a cross reference in the source file refering to the node under
;;; the cursor
(defvar para-main-menu-prefix "*Menu for: "
"Prefix of name of temporary buffer displaying the main menu.")
(defvar para-main-menu-buffer-mode-map nil
"Keymap used in temporary buffer displaying the main menu.")
;;; !!! remove this setq after testing
(setq para-main-menu-buffer-mode-map nil)
(if para-main-menu-buffer-mode-map
nil
(setq para-main-menu-buffer-mode-map (copy-keymap text-mode-map))
(define-key para-main-menu-buffer-mode-map "n" 'next-line)
(define-key para-main-menu-buffer-mode-map " " 'next-line)
(define-key para-main-menu-buffer-mode-map "p" 'previous-line)
(define-key para-main-menu-buffer-mode-map "\177" 'previous-line)
(define-key para-main-menu-buffer-mode-map "q"
'(lambda () "Bury current buffer; switch to current Para mode buffer."
(interactive)
(bury-buffer (current-buffer))
(switch-to-buffer para-current-buffer)))
(define-key para-main-menu-buffer-mode-map "\C-c\C-p\C-m"
'para-main-menu-goto-node)
(define-key para-main-menu-buffer-mode-map "m"
'para-main-menu-goto-node)
(define-key para-main-menu-buffer-mode-map "\C-c\C-pr"
'para-main-menu-buffer-xref)
(define-key para-main-menu-buffer-mode-map "r"
'para-main-menu-buffer-xref))
(defvar para-main-menu-completions-list nil
"List used by `para-main-menu-goto-node' for completions.")
;;; Menu goto function for `para-directory'
;;; !!! check whether this handles menus of form:
;;; * item:(filename)nodename. description
(defun para-main-menu-goto-node (menu-item)
"Go to the Para mode node prompted for; default is node under cursor.
Completion."
(interactive
(list
(let ((default
(save-excursion
(beginning-of-line)
(re-search-forward "\\* \\([^:\t\n]*\\):" nil t)
(buffer-substring (match-beginning 1) (match-end 1))))
item)
(setq item (let ((completion-ignore-case t))
(completing-read (if default
(format "Node name (default %s): " default)
"Node name: ")
para-main-menu-completions-list nil t)))
(if (string= item "") default item))))
(let ((file-name (para-extract-menu-file-name))
(node-name (para-extract-menu-name menu-item)))
(pop-to-buffer (get-file-buffer file-name))
(if (not (para-goto node-name))
(error "Node not found: %s " node-name))))
(defun para-extract-menu-file-name ()
"Return name of Para mode file for which this is the menu buffer."
(let ((menu-file (buffer-name)))
(substring menu-file
(progn (string-match para-main-menu-prefix menu-file)
(match-end 0)) -1)))
;;; `para-directory' cross reference insertion
(defun para-main-menu-buffer-xref
(reference &optional topic-description)
"Insert cross reference in Texinfo file to name of node under cursor.
The function inserts a Texinfo cross reference in the associated Para
mode file at the current position of point in the Para mode file.
Default is the name of the node under the cursor. Completion.
Optional second arg is topic description; with prefix arg if
interactive, prompt for topic description."
(interactive
(let ((default
(save-excursion
(para-extract-menu-name
(progn
(beginning-of-line)
(re-search-forward "\\* \\([^:\t\n]*\\):" nil t)
(buffer-substring (match-beginning 1) (match-end 1))))))
(description "")
node-name)
(setq node-name
(let ((completion-ignore-case t))
(completing-read
(if default
(format "Node name (default %s): " default)
"Node name: ")
para-main-menu-completions-list nil t)))
(if (string= node-name "") (setq node-name default))
(if current-prefix-arg
(setq description
(read-string
"Topic description (end with RET): "
(save-excursion
(goto-char (point-min))
(if (search-forward node-name nil t)
(para-main-menu-buffer-copy-description
(point-max)))))))
(list node-name description)))
(pop-to-buffer (get-file-buffer (para-extract-menu-file-name)))
(if (string= "" topic-description)
(insert (format "@xref{%s}." reference))
(insert (format "@xref{%s, , %s}." reference topic-description))))
;;; merge with texinfo-menu-copy-old-description
;;; this function handles the texinfo-master-menu-header
(defun para-main-menu-buffer-copy-description (end-of-menu)
"Return description field of menu line as a string.
Point must be located on the same line before the description. Point
left before description. Single argument, END-OF-MENU, is position
limiting search."
(skip-chars-forward "[:.,\t\n ]+")
; (re-search-forward ":[^:.,\t\n]*[:.,\t\n]+")
;; don't copy a carriage return at line beginning with asterisk!
(if (and (looking-at "\\w+")
(not (looking-at
(concat
"\\("
"^[ \t]\\* " ; beginning of menu line
"\\|"
"^@end menu" ; end of menu
"\\|"
texinfo-master-menu-header ; header line
"\\)"))))
(buffer-substring
(point)
(save-excursion
(if (not (if (re-search-forward
(concat
"\\(" ; beginning of menu line
"^[ \t]*\\* " ; works with master directory
"\\|"
"^@end menu" ; end of menu
"\\|" ; text at beginning of line
"^\\w+" ; is usually not part of desc.
"\\|"
texinfo-master-menu-header ; header line
"\\)")
end-of-menu t)
(goto-char (match-beginning 1))))
(goto-char end-of-menu))
(forward-line -1)
(while (looking-at "[ \t]*$")
(forward-line -1))
(end-of-line) ; go to end of last description line
(point)))
""))
;;; Utility functions to create indented menus
;;; used by `para-make-master-menu'
(defun para-insert-indented-master-menu ()
"Insert an indented master menu in the current buffer."
(re-search-forward "^@menu")
(beginning-of-line)
(let* ((buffer-original-position (point))
(master-menu (para-list-directory-list-menus-indentedly)))
(goto-char buffer-original-position)
;; Delete existing menu; buffer must have ordinary top menu
(delete-region (point)
(save-excursion (re-search-forward "^@end menu") (point)))
;; Handle top of menu
(insert "@menu\n")
(while (car master-menu)
(insert (format "%s" (car master-menu)))
(setq master-menu (cdr master-menu)))
;; Finish menu
(insert "@end menu")))
(defun para-list-directory-list-menus-indentedly ()
"List node names and descriptions as indented menu entries.
Must start after first menu. Returns list of menu entries."
(let ((master-directory-list))
(while (re-search-forward "^@node" nil t)
(beginning-of-line)
(let ((spaces (para-list-directory-indent-spaces))
(this-node (texinfo-copy-node-name)))
(message "Getting menu entry for `%s' ... " this-node)
(save-excursion
(if (re-search-backward (concat "\\* " this-node ":") nil t)
(setq master-directory-list
(cons
(concat
(para-list-directory-indent-spaces)
(para-list-directory-menu-entry))
master-directory-list))
;; else no menu entry
)))
(end-of-line))
(nreverse master-directory-list)))
(defun para-list-directory-indent-spaces ()
"Return a string of spaces according to level of menu."
(cdr (assoc
(texinfo-hierarchic-level)
;; Perhaps this alist should be a variable rather than written in.
'(("chapter" . "")
("section" . " ")
("subsection" . " ")
("subsubsection" . " ")))))
(defun para-list-directory-menu-entry ()
"Return this menu entry, including description, as string.
Start at beginning of line."
(buffer-substring (point)
(save-excursion
(end-of-line)
(re-search-forward
(concat
"\\(\\* \\([^:\t\n]*\\):"
"\\|^@end menu"
"\\|"
texinfo-master-menu-header
"\\)")
nil
t)
(goto-char (match-beginning 0))
(beginning-of-line)
(point))))
;;; Display recently visited nodes in a temporary buffer
;;; History of visited nodes; easy cross reference making
;;; `para-list-visited-nodes' displays a list of the nodes you have
;;; visited in the Para mode file, most recent first. The nodes are
;;; displayed in a temporary buffer.
;;;
;;; `para-goto-visited-node' jumps you to the prompted for node in the
;;; Para Mode file. Default is the name of the node under the cursor.
;;; Completion."
;;;
;;; `para-visited-xref' inserts a cross reference in the current Para
;;; mode buffer at point in that buffer. The default node name is the
;;; name of the node under the cursor in the `*Recently Visited
;;; Nodes*' buffer. Completion offered for other node names, but only
;;; for node names listed in the `*Recently Visited Nodes*' buffer
;;; This function makes it easier to insert cross references,
;;; expecially when you cannot readily remember the node names.
;;;
;;; With optional argument (prefix if interactive), Emacs prompts you
;;; for a topic description.
;;; Keybindings are:
;;;
;;; C-c C-p l para-list-visited-nodes
;;;
;;; For bindings inside the temporary buffer created by
;;; `para-list-visited-nodes', use
;;;
;;; C-c C-p r para-visited-xref (based on `para-xref')
;;; C-c C-p C-m para-goto-visited-node (parallel to `para-menu')
;;;
;;; (Note that the keybinding for `para-visited-xref' and
;;; `para-goto-visited-node' are defined locally in the temporary
;;; buffer, not in the para-mode-map.)
(defvar para-history nil
"List of names of recently visited nodes.")
(defvar para-current-buffer nil
"Current buffer in Para Mode.")
(defvar para-current-node nil
"Current Para Mode node.")
(defvar para-list-visited-nodes-syntax-table nil)
(if para-list-visited-nodes-syntax-table nil
(setq para-mode-syntax-table (make-syntax-table))
(modify-syntax-entry ?\" "\"" para-list-visited-nodes-syntax-table))
(defvar para-list-visited-nodes-mode-map nil
"Keymap used in node listing buffer in Para mode.")
;;; !!! remove this setq after testing
(setq para-list-visited-nodes-mode-map nil)
(if para-list-visited-nodes-mode-map
nil
(setq para-list-visited-nodes-mode-map (copy-keymap text-mode-map))
(define-key para-list-visited-nodes-mode-map "n" 'next-line)
(define-key para-list-visited-nodes-mode-map " " 'next-line)
(define-key para-list-visited-nodes-mode-map "p" 'previous-line)
(define-key para-list-visited-nodes-mode-map "\177" 'previous-line)
(define-key para-list-visited-nodes-mode-map "q"
'(lambda () "Bury current buffer; switch to current Para mode buffer."
(interactive)
(bury-buffer (current-buffer))
(switch-to-buffer para-current-buffer)))
(define-key para-list-visited-nodes-mode-map "\C-c\C-p\C-m"
'para-goto-visited-node)
(define-key para-list-visited-nodes-mode-map "m"
'para-goto-visited-node)
(define-key para-list-visited-nodes-mode-map "\C-c\C-pr"
'para-visited-xref)
(define-key para-list-visited-nodes-mode-map "r"
'para-visited-xref))
;;; List recently visited nodes.
;;; C-c C-p l para-list-visited-nodes
(defun para-list-visited-nodes ()
"Display a list of recently visited nodes in a temporary buffer.
In this buffer,
\\<para-list-visited-nodes-mode-map>
`para-visited-xref' is bound to \\[para-visited-xref]
which inserts a Texinfo cross reference in the associated Para mode
file at the current position of point in the Para mode file. Default
is the name of the node under the cursor. Completion is offered, but
only for node names listed in this temporary buffer.
With optional argument (prefix if interactive), Emacs prompts you
for a topic description.
`para-goto-visited-node' is bound to \\[para-goto-visited-node]
which will jump you to the prompted for node in the Para Mode file.
Default is the name of the node under the cursor. Completion offered."
(interactive)
(setq para-current-buffer (current-buffer))
;;; para-history is set in para-last and para-goto
(let ((visited-list para-history))
(with-output-to-temp-buffer "*Recently Visited Nodes*"
(pop-to-buffer "*Recently Visited Nodes*")
(kill-all-local-variables)
(use-local-map para-list-visited-nodes-mode-map)
(setq mode-name ""
major-mode 'para-list-visited-nodes-mode)
(set-syntax-table para-mode-syntax-table)
(while (car visited-list)
(insert (format "\"%s\"\n" (car visited-list)))
(setq visited-list (cdr visited-list))))))
(defun para-goto-visited-node (node-name)
"Go to the Para mode node prompted for; default is node under cursor.
Completion."
;;; Format of recently visited nodes buffer is the following:
;;;
;;; "Create Files and Nodes"
;;; "Movement Commands"
;;; "Shortcut Commands"
;;; "Para Mode Commands"
(interactive
(let ((default
(save-excursion
(beginning-of-line)
(re-search-forward "^\"\\([^\"]+\\)\"$" nil t)
(buffer-substring (match-beginning 1) (match-end 1))))
(description "")
completions)
(save-excursion ; create alist for completing-read
(goto-char (point-min))
(while (re-search-forward "^\"\\([^\"]+\\)\"$" nil t)
;; make a list each of element of which is \("name" . position\)
(setq completions (cons (cons (buffer-substring
(match-beginning 1)
(match-end 1))
(match-beginning 1))
completions))))
(setq node-name
(let ((completion-ignore-case t))
(completing-read
(if default
(format "Node name (default %s): " default)
"Node name: ")
completions nil t)))
(if (string= node-name "") (setq node-name default))
(list node-name)))
(pop-to-buffer para-current-buffer)
(if (not (para-goto node-name))
(error "Node not found: %s " node-name)))
(defun para-visited-xref (reference &optional topic-description)
"Insert cross reference to node cursor is on in Texinfo file.
The function inserts a Texinfo cross reference in the associated Para
mode file at the current position of point in the Para mode file.
Default is the name of the node under the cursor. Completion for
other node names listed in the buffer (not to all node names in source
file).
Optional second arg is topic description; with prefix arg if
interactive, prompt for topic description.
If temporary main menu buffer exists, default topic description is
description line in the menu. (Type \\[para-directory] to generate a
main menu buffer if it does not exist.\)"
;;; Format of recently visited nodes buffer is the following:
;;;
;;; "Create Files and Nodes"
;;; "Movement Commands"
;;; "Shortcut Commands"
;;; "Para Mode Commands"
(interactive
(let ((default
(save-excursion
(beginning-of-line)
(re-search-forward "^\"\\([^\"]+\\)\"$" nil t)
(buffer-substring (match-beginning 1) (match-end 1))))
(description "")
completions
node-name)
(save-excursion ; create alist for completing-read
(goto-char (point-min))
(while (re-search-forward "^\"\\([^\"]+\\)\"$" nil t)
;; make a list each of element of which is \("name" . position\)
(setq completions (cons (cons (buffer-substring
(match-beginning 1)
(match-end 1))
(match-beginning 1))
completions))))
(setq node-name
(let ((completion-ignore-case t))
(completing-read
(if default
(format "Node name (default %s): " default)
"Node name: ")
completions nil t)))
(if (string= node-name "") (setq node-name default))
(if current-prefix-arg
(setq description
(read-string
"Topic description (end with RET): "
(if (get-buffer para-main-menu-buffer-name)
(save-excursion
(set-buffer para-main-menu-buffer-name)
(goto-char (point-min))
(if (search-forward node-name nil t)
(para-main-menu-buffer-copy-description
(point-max))))))))
(list node-name description)))
(pop-to-buffer para-current-buffer)
(if (string= "" topic-description)
(insert (format "@xref{%s}." reference))
(insert (format "@xref{%s, , %s}." reference topic-description))))
;;; Movement commands with `C-c C-p' prefix: top, next, prev, etc.
; C-c C-p C-b para-beginning
; C-c C-p C-< para-top-node
; C-c C-p C-> para-final
; C-c C-p C-u para-up
; C-c C-p C-p para-prev
; C-c C-p C-n para-next
; C-c C-p C-[ para-goto-previous-node-in-sequence
; C-c C-p C-] para-goto-next-node-in-sequence
; C-c C-p RET para-menu
; C-c C-p C-l para-last
; C-c C-p C-g para-goto
; C-c C-p C-f para-follow
;;; Basic movement commands: top, next, prev, etc.
(defun para-beginning ()
"Go to the very beginning of the file (before a `Top' node)."
;;; Does not handle include files.
(interactive)
(widen)
(goto-char (point-min))
(setq para-history
(cons (save-excursion (para-extract-pointer 'next)) para-history)))
(defun para-top-node ()
"Go to the `Top' node."
(interactive)
(if (not (para-goto "top"))
(error "`Top' node not found.")))
(defun para-final ()
"Go to the last node in the Para file."
;;; Does not handle include files.
(interactive)
(widen)
(goto-char (point-max))
(re-search-backward "^@node")
(end-of-line)
(para-narrow-to-node)
(beginning-of-line)
(setq para-history
(cons (texinfo-copy-node-name)
para-history)))
(defun para-up ()
"Go to the superior node of this node."
(interactive)
(if (not (para-goto (para-extract-pointer 'up)))
(error "`Up' node not found.")))
(defun para-prev (&optional arg)
"Go to the previous node of this node.
With optional arg (prefix if interactive) go to the previous node in
sequence, regardless of its hierarcichal level or type."
(interactive "P")
(if arg
(para-goto (para-previous-node-in-sequence))
(if (not (para-goto (para-extract-pointer 'prev)))
(error "`Previous' node not found."))))
(defun para-next (&optional arg)
"Go to the next node of this node.
With optional arg (prefix if interactive) go to the next node in
sequence, regardless of its hierarcichal level or type."
(interactive "P")
(if arg
(para-goto (para-next-node-in-sequence))
(if (not (para-goto (para-extract-pointer 'next)))
(error "`Next' node not found."))))
(defun para-last ()
"Go back to the last node visited."
(interactive)
(or para-history
(error "This is the first para node you looked at"))
(let ((node-name (car (cdr para-history))))
(if (para-goto node-name)
(setq para-history (cdr (cdr para-history)))
(error "Node not found: %s " node-name))))
(defun para-extract-pointer (next-prev-up)
"Return name of `Next', `Previous', or `Up' pointer as a string."
;; only argument `next-prev-up' is 'next, 'previous, or 'up
(or (looking-at "^@node")
(re-search-backward "^@node" nil 'move-to-limit))
(widen)
(save-excursion
;; done this ugly way because of troubles with match-data when
;; trying to record all the matches for the next, prev and up
;; pointers and then using match-beginning and match-end to extract
;; desired name.
(cond ((eq next-prev-up 'next)
(if (not (looking-at "^@node")) ; not within a node
(para-next-node-in-sequence)
;; else within a node
;; search for end of `next' nodename
(if (re-search-forward (concat
"^@node"
"[ \t]*[^,]*[ \t]*,"
"[ \t]*\\([^,]*\\)[ \t]*,") nil t)
(buffer-substring
(match-beginning 1)
(progn (skip-chars-backward ", \t\n") (point)))
;; else
(error "Next node not found!"))))
((eq next-prev-up 'prev)
;; search for end of `previous' nodename
(if (re-search-forward (concat
"^@node"
"[ \t]*[^,]*[ \t]*," ; nodename
"[ \t]*[^,]*[ \t]*," ; `Next'
"[ \t]*\\([^,]*\\)[ \t]*,") nil t) ; `Prev'
(buffer-substring
(match-beginning 1)
(progn (skip-chars-backward ", \t\n") (point)))
;; else
(error "Previous node not found!")))
((eq next-prev-up 'up)
;; search for end of `up' nodename
(if (re-search-forward (concat
"^@node"
"[ \t]*[^,]*[ \t]*," ; nodename
"[ \t]*[^,]*[ \t]*," ; `Next'
"[ \t]*[^,]*[ \t]*," ; `Prev'
"[ \t]*\\([^,\n]*\\)" ; `Up'
"[ \t]*$") nil t)
(buffer-substring
(match-beginning 1)
(progn (skip-chars-backward ", \t\n") (point)))
;; else
(error "Up node not found!"))))))
(defun para-goto-next-node-in-sequence ()
"Go to the next node in buffer regardless of its hierarchical level.
This is an interface to the function `para-next-node-in-sequence'."
(interactive)
(para-goto (para-next-node-in-sequence)))
(defun para-goto-previous-node-in-sequence ()
"Go to the previous node in buffer regardless of its hierarchical level.
This is an interface to the function `para-previous-node-in-sequence'."
(interactive)
(para-goto (para-previous-node-in-sequence)))
(defun para-follow (reference)
"Follow REFERENCE to the node it refers to.
REFERENCE may include a filename or be an abbreviation of the reference name."
(interactive
(list
(let ((default
(save-excursion
(beginning-of-line)
(if (re-search-forward "@p?x?ref{[ \t]*\\([^,}]*\\)" nil t)
(buffer-substring (match-beginning 1) (match-end 1))
(error "No cross-reference in this node"))))
completions
nodename
filename
current-point
i)
(save-excursion
(goto-char (point-min))
(while
;; search for cross reference
(re-search-forward "@p?x?ref{[ \t]*\\([^,}]*\\)" nil t)
(setq current-point (point))
(setq nodename
(buffer-substring
(match-beginning 1)
(progn (skip-chars-backward " \t\n") (point))))
;; remove extraneous characters from nodename string
(while (setq i (string-match "[ \n\t]+" nodename i))
(setq nodename (concat (substring nodename 0 i) " "
(substring nodename (match-end 0))))
(setq i (1+ i)))
(skip-chars-forward " \t\n")
(if (looking-at "}") ; no filename entry
(setq filename nil)
;; else not looking at a closing brace
;; possibilities: is a two, three, four, or five entry xref
;; set up for search
(re-search-backward "@p?x?ref{" nil t)
(cond (;; search for xref ending with second entry
(re-search-forward "@p?x?ref{[^,]*,[^,]*}" nil t)
(setq filename nil))
;; search for xref ending with third entry
((re-search-forward "@p?x?ref{[^,]*,[^,]*,[^,]*}" nil t)
(setq filename nil))
;; search for xref ending with filename \(fourth entry\)
((re-search-forward
"@p?x?ref{[^,]*,[^,]*,[^,]*,\\([^,]*\\)}" nil t)
(setq filename
(buffer-substring
(progn
(goto-char (match-beginning 1))
(skip-chars-forward " \t\n")
(point))
(progn
(goto-char (match-end 1))
(skip-chars-forward " \t\n")
(point)))))
;; else look for xref ending in manual name entry
((re-search-forward
"@p?x?ref{[^,]*,[^,]*,[^,]*,\\([^,]*\\)\\(,[^,]+\\)}"
nil t)
(setq filename
(buffer-substring
(progn
(goto-char (match-beginning 1))
(skip-chars-forward " \t\n")
(point))
(progn
(goto-char (match-end 1))
(skip-chars-forward " \t\n")
(point)))))
(t (setq filename nil))))
(if (assoc nodename completions)
nil
(setq completions (cons (cons nodename filename) completions)))
(goto-char current-point)))
(cond (completions
(let ((completion-ignore-case t))
(setq reference
(completing-read
(if default
(format "Follow cross ref to (default %s): " default)
"Follow cross ref to: ")
completions nil t)))
(if (string= reference "") (setq reference default))
(if (cdr (assoc reference completions))
(setq filename
(read-from-minibuffer
"Go to cross reference in file: "
(cdr (assoc reference completions))))))
(t (error "No cross-reference in this node")))
(and filename (setq filename (concat "(" filename ")")))
(concat filename reference))))
(para-goto reference))
;;; Regular Expression Search
(defvar para-last-search nil
"Default regexp for para-search command to search for.")
(defun para-search (regexp)
"Search for REGEXP, starting from point, and select node it's found in."
(interactive "sSearch (regexp): ")
(if (equal regexp "")
(setq regexp para-last-search)
(setq para-last-search regexp))
(let ((this-node para-current-node)
found)
(save-excursion
(save-restriction
(widen)
(re-search-forward regexp)
(setq found (point))))
(widen)
(goto-char found)
(para-narrow-to-node)
;; para-narrow-to-node specifies value of para-current-node
(or (equal this-node para-current-node)
(setq para-history (cons para-current-node para-history)))
(para-set-mode-line)))
;;; Name of next or previous node in strict sequence
(defun para-next-node-in-sequence ()
"Return name of next node in buffer regardless of its hierarchical level."
(interactive)
(widen)
(end-of-line) ; to skip past `@node' at beginning of node.
(if (not (re-search-forward "^@node" nil t))
(progn (beginning-of-line)
(para-narrow-to-node)
(error "Next node not found!"))
(beginning-of-line)
(let ((nodename (texinfo-copy-node-name)))
(or nodename
(error "No node name found for Next node!")))))
(defun para-previous-node-in-sequence ()
"Go to the previous node in buffer regardless of its hierarchical level."
(interactive)
(para-narrow-to-node) ; so back search is not stuck at current node
(goto-char (point-min))
(widen)
(if (not (re-search-backward "^@node" nil t))
(progn (beginning-of-line)
(para-narrow-to-node)
(error "Previous node not found!"))
(let ((nodename (texinfo-copy-node-name)))
(or nodename
(error "No node name found for Previous node!")))))
;;; Single keystroke Info commands
(defun para-@ (key)
"Provide Info-like commands when point in upper-left or buffer read-only.
In a writable buffer when point is not in the upper-left corner, keys
self-insert."
(interactive "p")
(if (and (not buffer-read-only)
(pos-visible-in-window-p (1- (point))))
(progn
(self-insert-command key)
(if (<= fill-column (current-column))
(if auto-fill-hook
(funcall auto-fill-hook)
nil)))
(let* ((this-key (aref (this-command-keys) 0)))
(call-interactively
(cond ((= this-key ?g) 'para-goto-node)
((= this-key ?<) 'para-top-node)
((= this-key ?>) 'para-final)
((= this-key ?\[) 'para-goto-previous-node-in-sequence)
((= this-key ?\]) 'para-goto-next-node-in-sequence)
((= this-key ? ) 'para-scroll-up)
((= this-key ?\^?) 'para-scroll-down)
((and (<= ?0 this-key) (<= this-key ?9))
(cdr (assoc
this-key
(cdr (assoc 16 (cdr (assoc 3 para-mode-map)))))))
(t (cdr (assoc
(- this-key ?`)
(cdr (assoc 16 (cdr (assoc 3 para-mode-map))))))))))))
(defun para-scroll-up (arg)
"Scroll text of current window upward ARG lines.
Keep point in same relative position in window."
(interactive "P")
(let ((current-relative-position (- (window-point) (window-start))))
(scroll-up arg)
(goto-char (+ (window-start) current-relative-position))))
(defun para-scroll-down (arg)
"Scroll text of current window downward ARG lines.
Keep point in same relative position in window."
(interactive "P")
(let ((current-relative-position (- (window-point) (window-start))))
(scroll-down arg)
(goto-char (+ (window-start) current-relative-position))))
;;; Index entry commands: @cindex, @findex, etc.
;; Must redefine each index command to use `para-index' since
;; `texinfo-index' deletes the index line after reading it.
;; Hence the lack of shared code.
(put 'vindex 'para-format 'para-format-vindex)
(defun para-format-vindex () (para-index 'para-vindex))
(put 'cindex 'para-format 'para-format-cindex)
(defun para-format-cindex () (para-index 'para-cindex))
(put 'findex 'para-format 'para-format-findex)
(defun para-format-findex () (para-index 'para-findex))
(put 'pindex 'para-format 'para-format-pindex)
(defun para-format-pindex () (para-index 'para-pindex))
(put 'tindex 'para-format 'para-format-tindex)
(defun para-format-tindex () (para-index 'para-tindex))
(put 'kindex 'para-format 'para-format-kindex)
(defun para-format-kindex () (para-index 'para-kindex))
(defun para-index (indexvar)
(let ((arg (texinfo-parse-expanded-arg)))
(set indexvar
(cons (list arg (para-current-node))
(symbol-value indexvar)))))
(defconst para-indexvar-alist
'(("cp" . para-cindex)
("fn" . para-findex)
("vr" . para-vindex)
("tp" . para-tindex)
("pg" . para-pindex)
("ky" . para-kindex)))
;;; Define indices: @defindex @defcodeindex
(put 'defindex 'para-format 'para-format-defindex)
(put 'defcodeindex 'para-format 'para-format-defindex)
(defun para-format-defindex ()
(let* ((index-name (texinfo-parse-line-arg)) ; eg: `aa'
(indexing-command (intern (concat index-name "index")))
(index-formatting-command ; eg: `para-format-aaindex'
(intern (concat "para-format-" index-name "index")))
(index-alist-name ; eg: `para-aaindex'
(intern (concat "para-" index-name "index"))))
(set index-alist-name nil)
(put indexing-command ; eg, aaindex
'para-format
index-formatting-command) ; eg, para-format-aaindex
;; eg: "aa" . para-aaindex
(or (assoc index-name para-indexvar-alist)
(setq para-indexvar-alist
(cons
(cons index-name
index-alist-name)
para-indexvar-alist)))
(fset index-formatting-command
(list 'lambda 'nil
(list 'para-index
(list 'quote index-alist-name))))))
;;; Merge indices: @synindex @syncodeindex
(put 'synindex 'para-format 'para-format-synindex)
(put 'syncodeindex 'para-format 'para-format-synindex)
(defun para-format-synindex ()
(let* ((args (texinfo-parse-line-arg))
(second (cdr (read-from-string args)))
(joiner (symbol-name (car (read-from-string args))))
(joined (symbol-name (car (read-from-string args second)))))
(if (assoc joiner para-short-index-cmds-alist)
(put
(cdr (assoc joiner para-short-index-cmds-alist))
'para-format
(or (cdr (assoc joined para-short-index-format-cmds-alist))
(intern (concat "para-format-" joined "index"))))
(put
(intern (concat joiner "index"))
'para-format
(or (cdr(assoc joined para-short-index-format-cmds-alist))
(intern (concat "para-format-" joined "index")))))))
(defconst para-short-index-cmds-alist
'(("cp" . cindex)
("fn" . findex)
("vr" . vindex)
("tp" . tindex)
("pg" . pindex)
("ky" . kindex)))
(defconst para-short-index-format-cmds-alist
'(("cp" . para-format-cindex)
("fn" . para-format-findex)
("vr" . para-format-vindex)
("tp" . para-format-tindex)
("pg" . para-format-pindex)
("ky" . para-format-kindex)))
;;; Print index: @printindex
(put 'printindex 'para-format 'para-format-printindex)
(defun para-format-printindex ()
(let ((indexelts (symbol-value
(cdr (assoc (texinfo-parse-line-arg)
para-indexvar-alist))))
opoint)
(insert "\n@ignore\n* Menu:\n\n")
(setq opoint (point))
(para-print-index nil indexelts)
(if (eq system-type 'vax-vms)
(texinfo-sort-region opoint (point))
(shell-command-on-region opoint (point) "sort -fd" 1))
(insert "\n@end ignore\n")))
(defun para-print-index (file indexelts)
(while indexelts
(if (stringp (car (car indexelts)))
(insert "* " (car (car indexelts))
": " (if file (concat "(" file ")") "")
(nth 1 (car indexelts)) ".\n")
;; index entries from @include'd file
(para-print-index (nth 1 (car indexelts))
(nth 2 (car indexelts))))
(setq indexelts (cdr indexelts))))
;;; Insert index into Para mode file (interactive command)
(defun para-insert-index ()
"Insert index at @printindex command in Para mode file."
(interactive)
(require 'texinfmt)
(widen)
(let (para-command-name
texinfo-command-start
texinfo-command-end
texinfo-last-node ; Texinfo mode variable
para-vindex
para-findex
para-cindex
para-pindex
para-tindex
para-kindex)
(goto-char (point-min))
(while (re-search-forward "^@\\w+index" nil t)
(forward-word -1)
;; @ is followed by a command-word; find the end of the word.
(setq texinfo-command-start (point))
(forward-word 1)
(setq texinfo-command-end (point))
;; Call the handler for this command.
(setq para-command-name
(intern (buffer-substring texinfo-command-start
texinfo-command-end)))
(let ((cmd (get para-command-name 'para-format)))
(if cmd (funcall cmd)
(error "Para mode cannot handle \"%s\"."
(buffer-substring
texinfo-command-start
texinfo-command-end)))))))
;;; Create index entries (interactive commands)
(defun para-index-function (function &optional where)
"Insert entry for function index at end of paragraph.
Interactively, offer first symbol following point within paragraph
that is enclosed by braces of @code command. With prefix arg, insert
entry at point \(no default offered\)."
(interactive (para-index-what-and-where-utility))
(if (eq where 'end-para) (forward-paragraph 1))
(insert "@findex " function "\n"))
(defun para-index-variable (variable &optional where)
"Insert entry for variable index at end of paragraph.
Interactively, offer first symbol following point within paragraph
that is enclosed by braces of @code command. With prefix arg, insert
entry at point \(no default offered\)."
(interactive (para-index-what-and-where-utility))
(if (eq where 'end-para) (forward-paragraph 1))
(insert "@vindex " variable "\n"))
(defun para-index-program (program &optional where)
"Insert entry for program index at end of paragraph.
Interactively, offer first symbol following point within paragraph
that is enclosed by braces of @code command. With prefix arg, insert
entry at point \(no default offered\)."
(interactive (para-index-what-and-where-utility))
(if (eq where 'end-para) (forward-paragraph 1))
(insert "@pindex " program "\n"))
(defun para-index-datatype (datatype &optional where)
"Insert entry for datatype index at end of paragraph.
Interactively, offer first symbol following point within paragraph
that is enclosed by braces of @code command. With prefix arg, insert
entry at point \(no default offered\)."
(interactive (para-index-what-and-where-utility))
(if (eq where 'end-para) (forward-paragraph 1))
(insert "@tindex " program "\n"))
(defun para-index-keystroke (keystrokes &optional where)
"Insert entry for keystroke index at end of paragraph.
Interactively, offer default:
1. If point is at beginning of an @item or @itemx line,
offer rest of that line.
2. Else, offer first symbol following point within paragraph
that is enclosed by braces of @kbd command.
With prefix arg, insert entry at point \(no default offered\)."
(interactive
(let (keystrokes where)
(if current-prefix-arg
(setq keystrokes (read-from-minibuffer "Keystrokes: ")
where 'here)
(let((default-keystrokes
(if (looking-at "@itemx? \\(.*\\)$")
(buffer-substring (match-beginning 1) (match-end 1))
(if (re-search-forward "@kbd\\s(\\(\\S)+\\)\\s)")
(buffer-substring (match-beginning 1) (match-end 1))
""))))
(setq keystrokes
(read-from-minibuffer
"Keystrokes: "
(if default-keystrokes default-keystrokes ""))
where 'end-para)))
(list keystrokes where)))
(if (eq where 'end-para) (forward-paragraph 1))
(insert "@kindex " keystrokes "\n"))
(defun para-index-concept (concept &optional where)
"Insert entry for concept index at end of paragraph.
Offer word following point as default; with numeric prefix arg, offer
that many words following point as default. With prefix arg (not a
numeric prefix arg), insert entry at point \(no default offered\).
Non-interactively, first arg CONCEPT is string; optional second arg
WHERE is either symbol 'here or symbol 'end-para."
(interactive
(let (concept where how-many)
(cond ((and (listp current-prefix-arg)
(eq 4 (prefix-numeric-value current-prefix-arg)))
(setq where 'here))
((prefix-numeric-value current-prefix-arg)
(setq how-many (prefix-numeric-value current-prefix-arg)
where 'end-para))
(t (setq how-many 1 where 'end-para)))
(if (eq where 'end-para)
(let((default-concept
(buffer-substring
(save-excursion (skip-chars-forward " \t") (point))
(save-excursion (forward-word how-many) (point)))))
(setq concept (read-from-minibuffer
"Concept: " (if default-concept default-concept
""))))
(setq concept (read-from-minibuffer
"Concept: ")))
(list concept where)))
(if (eq where 'end-para) (forward-paragraph 1))
(insert "@cindex " concept "\n"))
;;; Index utility functions
;;; para-current-node, para-index-what-and-where-utility
(defun para-current-node ()
"Return name of current node at or preceding point."
(save-excursion
(if (or (looking-at "^@node")
(re-search-backward "^@node" nil 'move-to-limit))
;; point is within a node
(progn
(re-search-forward "@node[ \t]*\\<\\([^,]*\\),")
(buffer-substring (match-beginning 1) (match-end 1)))
;; else node not found
(error "Node not found."))))
(defun para-index-what-and-where-utility ()
"Utility used by para-index-function and others."
(let (what where)
(if current-prefix-arg
(setq what (read-from-minibuffer "Entry: ")
where 'here)
(let((default-what
(if (re-search-forward
"@code\\s(\\(\\S)+\\)\\s)"
(save-excursion (forward-paragraph 1) (point))
t)
(buffer-substring
(match-beginning 1) (match-end 1)))))
(setq what (read-from-minibuffer
"Entry: " (if default-what default-what ""))
where 'end-para)))
(list what where)))
(message "Loading Para mode ... done ")
(provide 'para)
;;; Bob only: site specific key rebinding
; `C-c C-x' is the prefix key for Para indexing.
; Redefine para-mode-map to use control keys in order to avoid
; conflict with Bob's keybindings for edebug.
(define-key para-mode-map "\C-c\C-x\C-c" 'para-index-concept)
(define-key para-mode-map "\C-c\C-x\C-f" 'para-index-function)
(define-key para-mode-map "\C-c\C-x\C-k" 'para-index-keystroke)
(define-key para-mode-map "\C-c\C-x\C-p" 'para-index-program)
(define-key para-mode-map "\C-c\C-x\C-t" 'para-index-datatype)
(define-key para-mode-map "\C-c\C-x\C-v" 'para-index-variable)
(define-key para-mode-map "\C-c\C-x\C-x" 'para-insert-index)